package yl.hs.bmipfull.service.report.impl;

import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import yl.hs.bmipfull.daos.material.IPurchaseReceiptDao;
import yl.hs.bmipfull.daos.material.IRawMaterialInfoDao;
import yl.hs.bmipfull.daos.production.IProductionRecordDao;
import yl.hs.bmipfull.daos.production.IProductionRecordDetailsDao;
import yl.hs.bmipfull.pojo.material.PurchaseReceiptEntity;
import yl.hs.bmipfull.pojo.material.RawMaterialInfoEntity;
import yl.hs.bmipfull.pojo.production.ProductionRecordDetailsEntity;
import yl.hs.bmipfull.pojo.production.ProductionRecordEntity;
import yl.hs.bmipfull.service.material.IPurchaseReceiptService;
import yl.hs.bmipfull.service.material.IRawMaterialCateService;
import yl.hs.bmipfull.service.report.IRawMaterialStatisticService;
import yl.hs.bmipfull.utils.CK;
import yl.hs.bmipfull.utils.HsApp;
import yl.hs.bmipfull.utils.SearchFilterEngine;
import yl.hs.bmipfull.utils.Tl;
import yl.hs.bmipfull.viewmodels.PageQuery;
import yl.hs.bmipfull.viewmodels.finance.OrderState;
import yl.hs.bmipfull.viewmodels.report.RawMaterialStatisticModel;
import yl.hs.bmipfull.viewmodels.report.RawPurchaseStatisticVO;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 作用:   <br/>
 * 创建时间: 2022-02-23 08:40 <br/>
 * 采购统计-service实现
 * @author 吴高丰
 */
@Service
public class RawMaterialStatisticServiceImpl implements IRawMaterialStatisticService {
    @Autowired
    IPurchaseReceiptService purchaseService;
    @Autowired
    IRawMaterialCateService rawMaterialCateService;


    @Override
    public List<Object> queryReceipt(PageQuery page,String productCateSN) {
        try {
            List<Object> result = new ArrayList<>();
            val purchaseReceiptEntities = purchaseService.queryList(page);
            Set<String> collect = purchaseReceiptEntities.stream().map(PurchaseReceiptEntity::getRmCateSN).collect(Collectors.toSet());
            collect.forEach(t -> {
                List<RawMaterialStatisticModel> list = new ArrayList<>();
                var cate = rawMaterialCateService.findBySn(t);
                if(cate==null){

                }else if(!"".equals(productCateSN)&&productCateSN!=null){
                    if(!cate.getUseProducts().contains(productCateSN)){

                    }
                }else{
                    //cate.getUseProducts().contains(productCateSN);
                    val queryAll = page.getQuery();
                    queryAll.put("rmCateSN",t);
                    page.setQuery(queryAll);
                    try {
                        val objectMap = cate.toMap();
                        val toList = purchaseService.queryList(page);
                        Function<PurchaseReceiptEntity, List<Object>> compositeKey = personRecord ->
                                Arrays.asList(personRecord.getRmCateSN(), personRecord.getRmCate(), personRecord.getRmName(), personRecord.getRmSpec(), personRecord.getRmSN());
                        Map<Object, List<PurchaseReceiptEntity>> map = toList.stream().collect(Collectors.groupingBy(compositeKey));
                        map.keySet().forEach(key -> {
                            var value = map.get(key);
                            BigDecimal netweight = BigDecimal.valueOf(0);
                            BigDecimal quantity = BigDecimal.valueOf(0);
                            BigDecimal reviewWeight = BigDecimal.valueOf(0);
                            for (PurchaseReceiptEntity purchaseReceiptEntity : value) {
                                netweight = netweight.add(purchaseReceiptEntity.getNetWeight());
                                quantity = quantity.add(purchaseReceiptEntity.getQuantity());
                                reviewWeight= reviewWeight.add(purchaseReceiptEntity.getReviewWeight());
                            }
                            list.add(new RawMaterialStatisticModel(value.get(0).getRmCate(),value.get(0).getRmName(),value.get(0).getRmSpec(),netweight,quantity,value.size(),reviewWeight));
                        });
                        objectMap.put("reportList",list);
                        result.add(objectMap);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public List<Object> querySupplierAnalysis(PageQuery page, String productCateSN) {
        List<Object> result=new ArrayList<>();
        //page.getQuery().put("rmCateSN",productCateSN);
        List<PurchaseReceiptEntity> supplierList=purchaseReceiptDao.queryGroupBySupplierSN(page.getQuery(), SearchFilterEngine.generateQueryList(page.getFilters(), PurchaseReceiptEntity.class),(List<Integer>)page.getQuery().get("states"));
        for (var entity: supplierList) {
            page.getQuery().put("supplierSN",entity.getSupplierSN());
            List<RawMaterialStatisticModel> rawList=purchaseReceiptDao.queryGroupByRaw(page.getQuery(), SearchFilterEngine.generateQueryList(page.getFilters(), PurchaseReceiptEntity.class), (List<Integer>)page.getQuery().get("states"));
            List<RawMaterialStatisticModel> resultList=new ArrayList<>();
            for (var Raw: rawList) {
                var rawCate=rawMaterialCateService.findBySn(Raw.getRmCateSN());
                if(rawCate!=null){
                    if(productCateSN==null||productCateSN.equals("")){
                        resultList.add(Raw);
                    }else{
                        if(rawCate.getUseProducts().contains(productCateSN)){
                            resultList.add(Raw);
                        }
                    }

                }

            }
            Map<String,Object> resultMap=new HashMap<>();
            resultMap.put("supplierName",entity.getSupplierName());
            resultMap.put("supplierSN",entity.getSupplierSN());
            resultMap.put("reportList",resultList);
            if(resultList.size()>0){
                result.add(resultMap);
            }

        }
        return result;
    }

    @Override
    public List<RawPurchaseStatisticVO> queryRawPurchaseStatistic(PageQuery page, String productCateSn) {
        List<RawPurchaseStatisticVO> result = new ArrayList<>();
        try {
            page.getQuery().put("productCateSn", productCateSn);
            List<PurchaseReceiptEntity> receiptList = purchaseService.queryList(page);
            receiptList.forEach(receipt -> {
                List<RawPurchaseStatisticVO> tempVOList = result.stream().filter(t -> receipt.getRmSN().equals(t.getRmSn())).toList();
                BigDecimal price = purchaseService.getRmPrice(receipt.getContractSN(), receipt.getRmSN(), receipt.getInTime());
                RawPurchaseStatisticVO statisticVo;
                if (tempVOList != null && tempVOList.size() >0 ){
                    statisticVo = tempVOList.get(0);
                    statisticVo.setQuantity(statisticVo.getQuantity().add(receipt.getNetWeight()));
                    statisticVo.setAveragePrice(statisticVo.getAveragePrice().add(price).divide(new BigDecimal(2), 2,RoundingMode.HALF_UP));
                    statisticVo.setTotalAmount(statisticVo.getQuantity().multiply(statisticVo.getAveragePrice()));
                } else {
                    statisticVo = new RawPurchaseStatisticVO();
                    statisticVo.setRmSn(receipt.getRmSN());
                    statisticVo.setRmName(receipt.getRmName());
                    statisticVo.setRmCateName(receipt.getRmCate());
                    statisticVo.setRmSpec(receipt.getRmSpec());
                    statisticVo.setQuantity(receipt.getNetWeight());
                    statisticVo.setAveragePrice(price);
                    statisticVo.setTotalAmount(statisticVo.getQuantity().multiply(statisticVo.getAveragePrice()));
                    result.add(statisticVo);
                }
            });


        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public List<Map<String, Object>> queryRawMaterialMonthData(int year, int month, String unit) {
        var cal = Calendar.getInstance();
        var monthStartTime = Tl.strToDate(year+"-"+String.format("%02d", month)+"-01 08:00");
        cal.setTime(monthStartTime);
        cal.add(Calendar.MONTH,1);
        var monthEndTime = cal.getTime();
        var receiptList = purchaseReceiptDao.queryUnitReceipts(unit,monthStartTime,monthEndTime);
        var rmInfoList = rmInfoDao.getAllInfo();
        var wjjSN = HsApp.getSystemSet(CK.AdmixtureSN);
        var commonRmInfoList = rmInfoList.stream().filter(t->!t.getRmCateSN().equals(wjjSN)).toList();

        var wjjReceiptList = receiptList.stream().filter(t->t.getRmCateSN().equals(wjjSN)).toList();
        var details = detailsDao.queryListByTimeAndUnit(unit,monthStartTime,monthEndTime);
        var proRecords = recordDao.queryUnitProductionRecord(unit,monthStartTime,monthEndTime);
        var rmGroup = commonRmInfoList.stream().collect(Collectors.groupingBy(RawMaterialInfoEntity::getRmName,Collectors.groupingBy(RawMaterialInfoEntity::getRmSpec,Collectors.toList())));
        List<Map<String,Object>> dataList = new ArrayList<>();
        var dayCount = (monthEndTime.getTime() - monthStartTime.getTime())/(24*60*60*1000);
        for (var rmName : rmGroup.keySet()){
            var rmSpecGroup = rmGroup.get(rmName);
            Map<String,Object> rmItem = new HashMap<>();
            rmItem.put("rmName",rmName);
            List<Map<String,Object>> specDataList = new ArrayList<>();
            for (var spec : rmSpecGroup.keySet()){
                var specList = rmSpecGroup.get(spec);
                Map<String,Object> specItem = new HashMap<>();
                specItem.put("spec",spec);
                var rmIDList = specList.stream().map(RawMaterialInfoEntity::getSn).toList();
                List<Map<String,Object>> dayDataList = new ArrayList<>();
                for (var i = 1; i<= dayCount; i++){
                    var dayStartTime = Tl.strToDate(year+"-"+String.format("%02d", month)+"-"+String.format("%02d", i)+" 08:00");
                    cal.setTime(dayStartTime);
                    cal.add(Calendar.DATE, 1);
                    var dayEndTime = cal.getTime();
                    Map<String,Object> dayItem = new HashMap<>();
                    BigDecimal receiptQuantity = receiptList.stream().filter(t->rmIDList.contains(t.getRmSN())
                            && t.getOutTime().compareTo(dayStartTime)>=0 && t.getOutTime().compareTo(dayEndTime) < 0).map(t->{
                                if(t.getState()>= OrderState.REVIEWED.code){
                                    return t.getReviewWeight();
                                }
                                else{
                                    return t.getNetWeight();
                                }
                    }).reduce(BigDecimal.ZERO,BigDecimal::add);
                    dayItem.put("receiptQuantity",receiptQuantity);
                    var dayRecordList = proRecords.stream().filter(t->t.getStartTime().compareTo(dayStartTime)>=0 && t.getStartTime().compareTo(dayEndTime)<0).map(ProductionRecordEntity::getId).toList();
                    var consumeQuantity = details.stream().filter(t->dayRecordList.contains(t.getProductionRecordId())
                    && rmIDList.contains(t.getRawMaterialSn())).map(ProductionRecordDetailsEntity::getConsumptionQuantity).reduce(BigDecimal.ZERO,BigDecimal::add);
                    dayItem.put("consumeQuantity",consumeQuantity);
                    dayItem.put("inventory",BigDecimal.ZERO);
                    dayDataList.add(dayItem);
                }
                specItem.put("datas",dayDataList);
                specDataList.add(specItem);
            }
            rmItem.put("childs",specDataList);
            dataList.add(rmItem);
        }
        var sGroup = wjjReceiptList.stream().collect(Collectors.groupingBy(PurchaseReceiptEntity::getSupplierName,
                Collectors.groupingBy(PurchaseReceiptEntity::getRmSN,Collectors.toList())));
        for(var s :sGroup.keySet()){
            var rGroup = sGroup.get(s);
            Map<String,Object> sItem = new HashMap<>();
            sItem.put("rmName",s);
            List<Map<String,Object>> specDataList = new ArrayList<>();
           for (var r : rGroup.keySet()){
                var rReceiptList = rGroup.get(r);
               Map<String,Object> rItem = new HashMap<>();
               String rmName="",rmSpec="";
               var rmInfoOpt = rmInfoList.stream().filter(t->t.getSn().equals(r)).findFirst();
               if(rmInfoOpt.isPresent()) {
                   RawMaterialInfoEntity rmInfo = rmInfoOpt.get();

                   rmSpec = rmInfo.getRmSpec();
               }
               rItem.put("spec",rmName+rmSpec);
               List<Map<String,Object>> dayDataList = new ArrayList<>();
               for (var i = 1; i<= dayCount; i++){
                   var dayStartTime = Tl.strToDate(year+"-"+month+"-"+String.format("%02d", i)+" 08:00");
                   cal.setTime(dayStartTime);
                   cal.add(Calendar.DATE, 1);
                   var dayEndTime = cal.getTime();
                   Map<String,Object> dayItem = new HashMap<>();
                   BigDecimal receiptQuantity = rReceiptList.stream().filter(t-> t.getOutTime().compareTo(dayStartTime)>=0 && t.getOutTime().compareTo(dayEndTime) < 0).map(t->{
                       if(t.getState()>= OrderState.REVIEWED.code){
                           return t.getReviewWeight();
                       }
                       else{
                           return t.getNetWeight();
                       }
                   }).reduce(BigDecimal.ZERO,BigDecimal::add);
                   dayItem.put("day",String.format("%02d", i));
                   dayItem.put("receiptQuantity",receiptQuantity);
                   var dayRecordList = proRecords.stream().filter(t->t.getStartTime().compareTo(dayStartTime)>=0 && t.getStartTime().compareTo(dayEndTime)<0).map(ProductionRecordEntity::getId).toList();
                   var consumeQuantity = details.stream().filter(t->dayRecordList.contains(t.getProductionRecordId())
                           && t.getRawMaterialSn().equals(r)).map(ProductionRecordDetailsEntity::getConsumptionQuantity).reduce(BigDecimal.ZERO,BigDecimal::add);
                   dayItem.put("consumeQuantity",consumeQuantity.divide(new BigDecimal(1000),RoundingMode.FLOOR));
                   dayItem.put("inventory",BigDecimal.ZERO);
                   dayDataList.add(dayItem);
               }
               rItem.put("datas",dayDataList);
               specDataList.add(rItem);
           }
            sItem.put("childs",specDataList);
            dataList.add(sItem);
        }
        return dataList;
    }

    @Autowired
    public void setPurchaseReceiptDao(IPurchaseReceiptDao purchaseReceiptDao) {
        this.purchaseReceiptDao = purchaseReceiptDao;
    }

    @Autowired
    public void setRmInfoDao(IRawMaterialInfoDao rmInfoDao) {
        this.rmInfoDao = rmInfoDao;
    }

    @Autowired
    public void setDetailsDao(IProductionRecordDetailsDao detailsDao) {
        this.detailsDao = detailsDao;
    }

    @Autowired
    public void setRecordDao(IProductionRecordDao recordDao) {
        this.recordDao = recordDao;
    }

    private IRawMaterialInfoDao rmInfoDao;
    private IPurchaseReceiptDao purchaseReceiptDao;
    private IProductionRecordDetailsDao detailsDao;
    private IProductionRecordDao recordDao;
}
